file_path = r'consumption.csv'
k = 3


def read_data(file_path)->list:
    """
    读取csv数据文件，返回list
    :param file_path:
    :return: 返回读取数据的list，list中每行为一个list
    """
    import csv
    with open(file_path, 'r') as f:
        reader = csv.reader(f)
        data_list = list(reader)
    return data_list[1::]


def get_distance(p1, p2)->float:
    """
    计算P1和P2两个点之间的欧几里得距离
    :param p1: 一个点（一行数据）
    :param p2: 另一个点（另一行数据）
    :return: 两点间的距离
    """
    import math
    return math.sqrt((float(p1[1]) - float(p2[1])) ** 2 + (float(p1[2]) - float(p2[2])) ** 2 + (float(p1[3]) - float(p2[3])) ** 2)


def divide_to_centre(data: list, cp_list: list)->list:
    """
    将data中的各点划归到最近的中心点中
    :param data: 原始数据
    :param cp_list: 中心点列表
    :return: 中心点对应的list，与cp_list顺序相同
    """
    res_list = list()
    for i in range(len(cp_list)):  # 构建每个中心点的list
        res_list.append(list())
    for row in data:
        min_index = 0  # 最近中心点索引
        min_distance = get_distance(cp_list[0], row)  # 最近距离
        for cp in cp_list[1::]:
            cur_distance = get_distance(cp, row)
            if cur_distance < min_distance:
                min_distance = cur_distance
                min_index += 1
        res_list[min_index].append(row)
    return res_list


def get_avg_point(rows: list)->list:
    """
    计算相应行的平均值（计算平均值点，平均值点的id都是'-1'）
    :param rows: 行列表
    :return: 平均值（平均值点，平均值点的id都是'-1'）
    """
    n = len(rows)
    s1 = 0.0  # 三个坐标点的和
    s2 = 0.0
    s3 = 0.0
    for r in rows:
        s1 += float(r[1])
        s2 += float(r[2])
        s3 += float(r[3])
    return ['-1', str(s1 / n), str(s2 / n), str(s3 / n)]


def get_new_centre_point(divide_data: list)->list:
    """
    获取新的中心点
    :param divide_data: 当前中心点分类的点list
    :return: 新的中心点list
    """
    new_cps = list()
    for cluster in divide_data:
        new_cps.append(get_avg_point(cluster))
    return new_cps


def print_clusters(clusters: list):
    """
    打印分类好的簇
    :param clusters: 分类好的簇
    :return:
    """
    cluster_index = 1
    for c in clusters:
        print('簇{}中各元素：'.format(cluster_index))
        count = 1
        for i in c:
            print('{}  '.format(i), end='')
            if count == len(c) or count % 6 == 0:  # 每打印6个元素换行一次
                print()
            count += 1
        cluster_index += 1


if __name__ == '__main__':
    data = read_data(file_path)
    cp_list = list()  # 中心点列表
    for i in range(k):  # 根据K值从数据中选取相应数量的中心点
        cp_list.append(data[i])
    iteration_count = 1
    while True:
        clusters = divide_to_centre(data, cp_list)
        cp_list = get_new_centre_point(clusters)  # 重新计算中心点
        clusters_new = divide_to_centre(data, cp_list)  # 用新的中心点重新聚类
        if clusters == clusters_new:
            print('迭代{}次后收敛，结果如下：'.format(iteration_count))
            print_clusters(clusters)
            break
        iteration_count += 1
